<!-- mosowe-canvas-image -->
<template>
	<view class="mosowe-canvas-image">
		<view class="slot-view" @click="createCanvas">
			<slot></slot>
		</view>
		<view class="canvas-wrap-box">
			<!-- 主面板绘制 -->
			<canvas class="canvas-wrap" canvas-id="canvas"
				:style="'width: ' + width + 'px; height: ' + height + 'px;'"></canvas>
			<!-- 这个是用来绘制圆形/矩形图片的 -->
			<canvas class="canvas-wrap" canvas-id="canvas-arc" :style="
          'width: ' + canvasArcWidth + 'px; height: ' + canvasArcHeight + 'px;'
        "></canvas>
		</view>
	</view>
</template>

<script>
	import QR from './wxqrcode.js';
	export default {
		name: 'mosowe-canvas-image',
		props: {
			imgType: {
				// 图片类型
				type: String,
				default: 'jpg',
				validator: () => {
					return ['jpg', 'png'];
				}
			},
			showLoading: {
				// 是否开启加载中
				type: Boolean,
				default: false
			},
			compress: {
				// 是否开启压缩
				type: Boolean,
				default: false
			},
			compressSize: {
				// 压缩界限，超过界限压缩，默认2M
				type: [Number, String],
				default: 1024 * 1024 * 2
			},
			showPreview: {
				// 生成图像后是否预览
				type: Boolean,
				default: false
			},
			height: {
				// canvas高度
				type: [String, Number],
				default: 200
			},
			width: {
				// canvas宽度
				type: [String, Number],
				default: 200
			},
			lists: {
				type: Array,
				default: () => {
					return [];
				}
			},
			qrSize: {
				// 二维码size
				type: Number,
				default: 300
			},
			qrTypeNumber: {
				// 二维码typeNumber
				type: Number,
				default: 10
			}
		},
		data() {
			return {
				canvas: null,
				listsIndex: 0,
				listsLength: 0,
				canvasArc: null,
				canvasArcWidth: 100,
				canvasArcHeight: 100,
				compressQuality: 20,
				compressQualityH5: 5,
				qrCacheFileList: []
			};
		},
		// 组件创建完成
		created() {
			this.canvas = uni.createCanvasContext('canvas', this);
			this.canvasArc = uni.createCanvasContext('canvas-arc', this);
		},
		// 页面方法
		methods: {
			// 开始绘制
			createCanvas() { 
				this.clearCanvas(); 
				if (this.lists.length === 0) {
					uni.showToast({
						title: 'lists不能为空',
						icon: 'none'
					});
					return;
				}
				this.listsIndex = 0;
				this.listsLength = this.lists.length - 1;
				if (this.showLoading) {
					uni.showLoading({
						title: '正在生成图片...',
						mask: true
					});
				}
				this.dataDrawCanvas();
			},
			// 数据绘制
			dataDrawCanvas() {
				let item = { ...this.lists[this.listsIndex] };
				if (item.type === 'image') {
					item.content = item.localImage || item.content;
					// 图片
					if (item.content.indexOf('https://') > -1||item.content.indexOf('http://') > -1) {
						// https://网络图片
						this.downloadImageNotH5(item);
					} else {
						// 本地选择图片
						if (
							this.compress &&
							item.hasOwnProperty('file') &&
							item.file.size > this.compressSize
						) {
							// 大于限制2M压缩
							this.compressImage(item);
						} else {
							this.drawMethodsImage(item);
						}
					}
				} else if (item.type === 'text') {
					// 文本
					this.drawText(item);
				} else if (item.type === 'shape') {
					// 矩形/圆角矩形/圆心/椭圆形
					this.drawShapeMain(item);
				} else if (item.type === 'qr') {
					// 二维码
					this.drawQR(item);
				} else if (item.type === 'image_base64') {


					this.drawBase64(item)
				}
			},

			// https图片下载本地并绘制
			downloadImageNotH5(item) {
				uni.downloadFile({
					url: item.content,
					header: {
						'Access-Control-Allow-Origin': '*'
					},
					success: (res) => {
						item.content = res.tempFilePath;
						this.lists[this.listsIndex].localImage = res.tempFilePath;
						if (item.imageShapeType) {
							uni.getImageInfo({
								src: res.tempFilePath,
								success: (r) => {
									item.imageWidth = r.width;
									item.imageHeight = r.height;
									item.whScale = r.width / r.height;
									this.lists[this.listsIndex].whScale = item.whScale;
									this.drawMethodsImage(item);
								}
							});
						} else {
							this.drawMethodsImage(item);
						}
					},
					fail: (err) => { 
					}
				});
			},
			// 图片渲染方式
			drawMethodsImage(item) {
				if (Object.keys(item).includes('arcR') || item?.ellipse) {
					this.drawShapeImage(item);
				} else {
					this.drawImage(item);
				}
			},
			// 图片压缩
			compressImage(item) {
				if (this.showLoading) {
					uni.showLoading({
						title: '压缩中...',
						mask: true
					});
				}
				// 非H5压缩
				// #ifndef H5
				uni.compressImage({
					src: item.content,
					quality: this.compressQuality,
					success: (res) => {
						if (this.showLoading) {
							uni.showLoading({
								title: '正在生成图片...',
								mask: true
							});
						}
						item.content = res.tempFilePath;

						this.drawMethodsImage(item);
					},
					fail: (res) => {
						uni.showToast({
							title: '压缩失败',
							icon: 'none'
						});
					}
				});
				// #endif
				// H5压缩
				// #ifdef H5
				let image = new Image();
				image.setAttribute('crossOrigin', 'anonymous');
				image.crossOrigin = 'Anonymous';
				image.src = item.content;
				image.onload = () => {
					let canvas = document.createElement('canvas');
					canvas.width = item.width;
					canvas.height = item.height;
					let ctx = canvas.getContext('2d');
					ctx.drawImage(image, 0, 0, item.width, item.height);
					let dataURL = canvas.toDataURL('image/png');
					item.content = dataURL;
					if (Object.keys(item).includes('arcR') || item?.ellipse) {
						this.drawShapeImage(item);
					} else {
						this.drawImage(item);
					}
				};
				// #endif
			},
			// 图片绘制
			drawImage(item) {
				this.canvas.globalAlpha = item.hasOwnProperty('globalAlpha') ?
					item.globalAlpha :
					1;
				this.canvas.drawImage(
					item.content,
					item.x,
					item.y,
					item.hasOwnProperty('width') ? item.width : this.width,
					item.hasOwnProperty('height') ? item.height : this.height
				);

				this.checkDrawOver();
			},
			async drawBase64(item) { 
				// #ifdef MP-WEIXIN
				item['qr'] = await this.base64ToSave(item.content, Date.now().toString());
				// #endif
				// #ifndef MP-WEIXIN
				item['qr'] = item.content;
				// #endif 
				this.canvas.globalAlpha = item.hasOwnProperty('globalAlpha') ?
					item.globalAlpha :
					1;
				 this.canvas.drawImage(
				 	item.qr,
				 	item.x,
				 	item.y,
				 	item.hasOwnProperty('width') ? item.width : this.width,
				 	item.hasOwnProperty('height') ? item.height : this.height
				 );
				 
				 this.checkDrawOver();
			},
			// 文本绘制
			drawText(item) {
				this.canvas.setFillStyle(
					item.hasOwnProperty('color') ? item.color : '#000000'
				);
				if (item?.font) {
					this.canvas.font = item?.font;
				} else {
					this.canvas.font = '10px sans-serif';
					this.canvas.setFontSize(item.hasOwnProperty('size') ? item.size : 20);
				}

				this.canvas.setTextAlign(
					item.hasOwnProperty('align') ? item.align : 'left'
				);
				this.canvas.globalAlpha = item.hasOwnProperty('globalAlpha') ?
					item.globalAlpha :
					1;

				if (item.maxWidth) {
					this.canvas.fillText(item.content, item.x, item.y, item.maxWidth);
				} else {
					this.canvas.fillText(item.content, item.x, item.y);
				}
				this.checkDrawOver();
			},
			// 矩形，圆角矩形，圆形，椭圆绘制主函数
			async drawShapeMain(item) {
				await this.shapeDraw(this.canvas, item);
				if (item?.gradient?.length) {
					// 渐变颜色填充
					let grd = null;
					if (item?.gradientType === 'circular') {
						const r = item.arcR || item.width / 2;
						grd = this.canvas.createCircularGradient(item.x + r, item.y + r, r);
					} else {
						grd = this.canvas.createLinearGradient(
							item.x,
							item.y,
							item.width + item.x,
							item.height + item.y
						);
					}
					item.gradient.forEach((item) => {
						grd.addColorStop(item[0], item[1]);
					});

					this.canvas.setFillStyle(grd);
				} else {
					// 一般颜色填充
					const color = item.color || '#000000';
					this.canvas.setFillStyle(color);
				}
				this.canvas.fill();
				this.checkDrawOver();
			},
			// 绘制矩形/圆形，校验是否绘制椭圆
			shapeDraw(ctx, item, arcImage = false) {
				return new Promise(async (resolve) => {
					const x = arcImage ? 0 : item.x;
					const y = arcImage ? 0 : item.y;
					const w = item.width;
					const h = item.height;
					const r = item?.arcR || 0;
					const strokeColor = item?.strokeColor;
					const lineWidth = item?.lineWidth;

					if (item.ellipse) {
						await this.BezierEllipse2(ctx, x + w / 2, y + h / 2, w / 2, h / 2);
					} else {
						ctx.beginPath();
						ctx.moveTo(x + r, y);
						ctx.arcTo(x + w, y, x + w, y + h, r);
						ctx.arcTo(x + w, y + h, x, y + h, r);
						ctx.arcTo(x, y + h, x, y, r);
						ctx.arcTo(x, y, x + w, y, r);
						ctx.closePath();
					}

					// 边框线宽度
					if (lineWidth) {
						ctx.setLineWidth(lineWidth);
					}
					// 边框线颜色
					if (strokeColor) {
						ctx.setStrokeStyle(strokeColor);
						ctx.stroke();
					}
					resolve(true);
				});
			},
			// 绘制椭圆
			BezierEllipse2(ctx, x, y, a, b) {
				return new Promise((resolve) => {
					const k = 0.5522848,
						ox = a * k, // 水平控制点偏移量
						oy = b * k; // 垂直控制点偏移量
					//从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
					ctx.beginPath();
					ctx.moveTo(x - a, y);
					ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);
					ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);
					ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
					ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
					ctx.closePath();
					resolve(true);
				});
			},

			// 圆形/圆角矩形/椭圆【图片】绘制主函数
			async drawShapeImage(item) {
				this.canvasArc.clearRect(0, 0, this.canvasArcWidth, this.canvasArcHeight);
				await this.timeSleep();
				this.canvasArcWidth = item.width;
				this.canvasArcHeight = item.height;
				this.canvasArc.save();

				await this.timeSleep();

				await this.shapeDraw(this.canvasArc, item, true);

				this.canvasArc.clip();

				let w = item.width;
				let h = item.height;
				if (item.imageShapeType) {
					switch (item.imageShapeType) {
						case 'width':
							h = item.width / item.whScale;
							break;
						case 'height':
							w = item.height * item.whScale;
							break;

						default:
							if (typeof item.imageShapeType === 'number') {
								w = item.imageShapeType;
								h = w / item.whScale;
							}
							break;
					}
				}
				this.canvasArc.drawImage(
					item.content,
					item.arcX || 0,
					item.arcY || 0,
					w,
					h
				);
				this.canvasArc.draw(false, async () => {
					await this.timeSleep();
					uni.canvasToTempFilePath({
							x: 0,
							y: 0,
							width: item.width,
							height: item.height,
							fileType: 'png',
							canvasId: 'canvas-arc',
							success: (res) => {
								item.content = res.tempFilePath;
								this.drawImage(item);
							},
							fail: (res) => {
								console.log(res);
							},
							complete: () => {
								this.canvasArc.restore();
								this.canvasArc.fillRect(0, 0, 0, 0);
							}
						},
						this
					);
				});
			},

			// 二维码绘制
			async drawQR(item) {
				const base64 = QR.createQrCodeImg(item.content, {
					size: parseInt(this.qrSize),
					typeNumber: this.qrTypeNumber
				});
				// #ifdef MP-WEIXIN
				item['qr'] = await this.base64ToSave(base64, Date.now().toString());
				// #endif
				// #ifndef MP-WEIXIN
				item['qr'] = base64;
				// #endif
				this.canvas.globalAlpha = item.hasOwnProperty('globalAlpha') ?
					item.globalAlpha :
					1;
				this.canvas.drawImage(
					item.qr,
					item.x,
					item.y,
					item.hasOwnProperty('width') ? item.width : this.width,
					item.hasOwnProperty('height') ? item.height : this.height
				);

				this.checkDrawOver();
			},
			//base64转本地图片，将数据存储在本地,小程序端兼容处理
			base64ToSave(base64data, FILE_BASE_NAME = 'tmp_base64src') {
				const fsm = uni.getFileSystemManager();
				return new Promise((resolve, reject) => {
					//format这个跟base64数据的开头对应
					// const [, format, bodyData] =
					// /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
					// if (!format) {
					// 	reject(new Error('ERROR_BASE64SRC_PARSE'));
					// }
					const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.jpg`;
					this.qrCacheFileList.push(filePath); 
					const buffer = wx.base64ToArrayBuffer(base64data);
					fsm.writeFile({
						filePath,
						data: buffer,
						encoding: 'binary',
						success() {
							resolve(filePath);
						},
						complete(){},
						fail(err) {
							console.log(err)
							reject(new Error('ERROR_BASE64SRC_WRITE'));
						}
					});
				});
			},
			// 制作完海报后删除本地数据,小程序端兼容处理
			removeSave(filePath) {
				// 把文件删除后再写进，防止超过最大范围而无法写入
				const fsm = uni.getFileSystemManager(); //文件管理器
				fsm.unlink({
					filePath: filePath,
					success(res) {},
					fail(e) {
						console.log('readdir文件删除失败：', e);
					}
				});
				if (this.qrCacheFileList.length) {
					this.removeSave(this.qrCacheFileList.shift());
				}
			},
			// 判断是否绘制完
			checkDrawOver() {
				if (this.listsIndex < this.listsLength) {
					// lists未画完
					this.listsIndex++;
					this.dataDrawCanvas();
				} else {
					this.canvasImage();
				}
			},

			// 绘制到画布并生成图片
			canvasImage() {
				this.listsIndex = 0;
				this.canvas.draw(false, async () => {
					await this.timeSleep(500);
					uni.canvasToTempFilePath({
							x: 0,
							y: 0,
							width: Number(this.width),
							height: Number(this.height),
							fileType: this.imgType,
							canvasId: 'canvas',
							success: (res) => {
								this.$emit('canvasImage', res.tempFilePath);
								if (this.showPreview) {
									this.showPreviewFn(res.tempFilePath);
								}
							},
							fail: (res) => {
								console.log(res);
							},
							complete: () => {
								if (this.showLoading) {
									uni.hideLoading();
								}

								// #ifdef MP-WEIXIN

								if (this.qrCacheFileList.length) {
									this.removeSave(this.qrCacheFileList.shift());
								}

								// #endif
							}
						},
						this
					);
				});
			},
			// 预览图
			showPreviewFn(img) {
				uni.previewImage({
					current: 0,
					urls: [img]
				});
			},
			// 清空画布
			clearCanvas() {
				this.canvas.clearRect(0, 0, this.width, this.height);
			},
			// 延时
			timeSleep(time = 100) {
				return new Promise((resolve) => {
					let t = setTimeout(() => {
						clearTimeout(t);
						t = null;
						resolve(true);
					}, time);
				});
			}
		}
	};
</script>

<style lang="scss" scoped>
	.mosowe-canvas-image {
		overflow: hidden;

		.canvas-wrap-box {
			overflow: hidden;
			height: 0;
			width: 0;
			position: fixed;
			left: 200%;
			top: 0;
		}

		.canvas-wrap {
			overflow: hidden;
			height: 0;
			width: 0;
		}
	}
</style>